package TimeLine;

import java.lang.reflect.Method;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;



/**
 * Classe Action définissant une action exécutée par le Timeline.
 */
public class Action extends TimerTask implements Cloneable {
    /**
    * Nombre de milisecond entre deux lancement
    */
    private int delay;
    /**
     * Object de l'action
     */
    private Object obj;
    /**
     * Methode de l'action a lancer
     */
    private String method;
    /**
     * Retourne l'objet de l'action
     * @return this.obj
     */
    public Object getObject ( ) {
        return this.obj;
    }
    /**
    * Retourne le nom de la methode a lancer
    * @return this.method
    */
    public String getMethod ( ) {
        return this.method;
    }
    /**
     * Retourne la valeur du delay de l'asction
     * @return this.delay
     */
    public int getDelay() {
        return this.delay;
    }
    /**
     * Constructeur
     * @param        obj Objet de l'action
     * @param        method Methode de l'action à lancer
     * @param	     delay Delay de l'action
     */
    public Action(Object obj, String method, int delay ) {
        this.obj = obj;
        this.method = method;
        this.delay = delay;
    }
    /**
     * Méthode pour cloner une action.
     * @return une nouvelle action
     */
    @Override
    public Action clone() {
        return new Action(this.obj,this.method,this.delay);
    }

    /**
    * Méthode de lancement de l'action.
    * @throws Exception Quand il y a eu une erreur dans le lancement de la methode
    */
    public void launch() throws Exception {
        try {
            // Récupération du vrai type de la classe.
            Class thisClass = Class.forName(this.obj.getClass().getName());

            // Récupération de la méthode à exécuter. (Par défaut sans paramètre)
            Method thisMethod = thisClass.getDeclaredMethod(method, new Class[0]);

            // Exécution dynamique de la méthode sur l'objet.
            thisMethod.invoke(this.obj, new Object[0]);
        } catch (Exception ex) {
            throw new Exception("Une erreur est survenue lors de l'appel de la méthode \"" + this.method + "\" sur le objet de type " + this.obj.getClass() + ".", ex);
        }
    }
    /**
     * Methode pour verifier l'égalité entre deux Actions
     * @param object L'action a comparé
     * @return Vrai si les deux actions sont identique si non faux
     */
    @Override
    public boolean equals(Object object) {
        if (object instanceof Action) {
            Action tmp = (Action) object;
            return (this.hashCode() == tmp.hashCode());
        } else {
            return false;
        }
    }
    /**
     * Donne le hashCode d'une action
     * @return Le hashCode de l'action
     */
    @Override
    public int hashCode() {
        int hash = 3;
        hash = 23 * hash + (this.obj != null ? this.obj.hashCode() : 0);
        hash = 23 * hash + (this.method != null ? this.method.hashCode() : 0);
        return hash;
    }
    /**
     * Methode de lancement de l'action
     */
    @Override
    public void run() {
        try {
            this.launch();
        } catch (Exception ex) {
            Logger.getLogger(Action.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
